<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<style>
body {padding:0px}
</style>
</head>
<body>
<canvas id="canvas" height="400" width="800"></canvas>
<script>
$(function($){
  function HCF(n0,n1){
    var dividend,divisor,remainder;
    if(Math.abs(n0) < Math.abs(n1)){
      divisor = n1;
      remainder = n0;
    }else{
      divisor = n0;
      remainder = n1;
    }
    while(remainder){
      dividend = divisor;
      divisor = remainder;
      remainder = dividend % divisor;
    }
    return Math.abs(divisor);
  }

  function Rational(numerator,denominator){
    if(Math.floor(numerator) !== numerator || Math.floor(denominator) !== denominator)
      throw new Error("wrong argument format!");
    if(numerator > 9007199254740992 || numerator < -9007199254740992
       || denominator > 9007199254740992 || denominator < -9007199254740992)
      throw new OverflowError("overflow!");
    this.numerator = numerator;
    this.denominator = denominator;
  }
  Rational.prototype = {
    numerator: undefined,
    denominator: undefined,
    add: function add(r){
      var hcf,thisDenominator,rDenominator;
      var numerator, denominator;
      if(this.denominator !== 0 && r.denominator !== 0){
        hcf = HCF(this.denominator,r.denominator);
        thisDenominator = this.denominator / hcf;
        rDenominator = r.denominator / hcf;
        if(Rational._checkMulOverflow(this.numerator, rDenominator) ||
           Rational._checkMulOverflow(r.numerator, thisDenominator) ||
           Rational._checkAddOverflow(this.numerator * rDenominator, r.numerator * thisDenominator) ||
           Rational._checkMulOverflow(this.denominator, rDenominator))
          throw new OverflowError("overflow!");
        numerator = this.numerator * rDenominator + r.numerator * thisDenominator;
        denominator = thisDenominator * hcf * rDenominator;
      } else {
        numerator = this.numerator * r.denominator + r.numerator * this.denominator;
        denominator = this.denominator * r.denominator;
      }      

      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    sub: function sub(r){
      var hcf,thisDenominator,rDenominator;
      var numerator, denominator;
      if(this.denominator !== 0 && r.denominator !== 0){
        hcf = HCF(this.denominator,r.denominator);
        thisDenominator = this.denominator / hcf;
        rDenominator = r.denominator / hcf;
        if(Rational._checkMulOverflow(this.numerator, rDenominator) ||
           Rational._checkMulOverflow(r.numerator, thisDenominator) ||
           Rational._checkSubOverflow(this.numerator * rDenominator, r.numerator * thisDenominator) ||
           Rational._checkMulOverflow(this.denominator, rDenominator))
          throw new OverflowError("overflow!");
        numerator = this.numerator * rDenominator - r.numerator * thisDenominator;
        denominator = thisDenominator * hcf * rDenominator;
      } else {
        numerator = this.numerator * r.denominator - r.numerator * this.denominator;
        denominator = this.denominator * r.denominator;
      }      

      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    mul: function mul(r){
      var hcf0,hcf1,thisDenominator,rNumerator,thisNumerator,rDenominator;
      var numerator, denominator;
      if(this.denominator !== 0 && r.denominator !== 0 && this.numerator !== 0 && r.numerator !== 0){
        hcf0 = HCF(this.denominator,r.numerator);
        thisDenominator = this.denominator / hcf0;
        rNumerator = r.numerator / hcf0;
        hcf1 = HCF(this.numerator,r.denominator);
        thisNumerator = this.numerator / hcf1;
        rDenominator = r.denominator / hcf1;
        if(Rational._checkMulOverflow(thisNumerator, rNumerator) ||
           Rational._checkMulOverflow(thisDenominator, rDenominator))
          throw new OverflowError("overflow!");
        numerator = thisNumerator * rNumerator;
        denominator = thisDenominator * rDenominator;
      } else {
        numerator = this.numerator * r.numerator;
        denominator = this.denominator * r.denominator;
      }      

      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    div: function div(r){
      var hcf0,hcf1,thisDenominator,rNumerator,thisNumerator,rDenominator;
      var numerator, denominator;
      if(this.denominator !== 0 && r.denominator !== 0 && this.numerator !== 0 && r.numerator !== 0){
        hcf0 = HCF(this.denominator,r.denominator);
        thisDenominator = this.denominator / hcf0;
        rDenominator = r.denominator / hcf0;
        hcf1 = HCF(this.numerator,r.numerator);
        thisNumerator = this.numerator / hcf1;
        rNumerator = r.numerator / hcf1;
        if(Rational._checkMulOverflow(thisNumerator, rDenominator) ||
           Rational._checkMulOverflow(thisDenominator, rNumerator))
          throw new OverflowError("overflow!");
        numerator = thisNumerator * rDenominator;
        denominator = thisDenominator * rNumerator;
      } else {
        numerator = this.numerator * r.denominator;
        denominator = this.denominator * r.numerator;
      }      

      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    mod: function mod(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator) ||
         Rational._checkMulOverflow(this.denominator, r.denominator))
        throw new OverflowError("overflow!");
      var numerator = this.numerator * r.denominator % (r.numerator * this.denominator);
      var denominator = this.denominator * r.denominator;
      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    addInt: function addInt(n){
      if(Rational._checkMulOverflow(n, this.denominator) ||
         Rational._checkAddOverflow(this.numerator, n * this.denominator))
        throw new OverflowError("overflow!");
      var numerator = this.numerator + n * this.denominator;
      var denominator = this.denominator;
      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    subInt: function subInt(n){
      if(Rational._checkMulOverflow(n, this.denominator) ||
         Rational._checkSubOverflow(this.numerator, n * this.denominator))
        throw new OverflowError("overflow!");
      var numerator = this.numerator - n * this.denominator;
      var denominator = this.denominator;
      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    mulInt: function mulInt(n){
      var hcf,thisDenominator;
      var numerator, denominator;
      if(this.denominator !== 0 && n !== 0){
        hcf = HCF(this.denominator,n);
        thisDenominator = this.denominator / hcf;
        n = n / hcf;
        if(Rational._checkMulOverflow(this.numerator, n))
          throw new OverflowError("overflow!");
        numerator = this.numerator * n;
        denominator = thisDenominator;
      } else {
        numerator = this.numerator * n;
        denominator = this.denominator;
      }      
      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    divInt: function divInt(n){
      var hcf,thisNumerator;
      var numerator, denominator;
      if(this.numerator !== 0 && n !== 0){
        hcf = HCF(this.numerator,n);
        thisNumerator = this.numerator / hcf;
        n = n / hcf;
        if(Rational._checkMulOverflow(this.denominator, n))
          throw new OverflowError("overflow!");
        numerator = thisNumerator;
        denominator = this.denominator * n;
      } else {
        numerator = this.numerator;
        denominator = this.denominator * n;
      }      

      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    modInt: function modInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
        throw new OverflowError("overflow!");
      var numerator = this.numerator % (n * this.denominator);
      var denominator = this.denominator;
      if(numerator !== 0){
        if(denominator !== 0){
          var hcf = HCF(numerator,denominator)
          numerator /= hcf;
          denominator /= hcf;
        } else if(numerator > 0)
          numerator = 1;
        else if(numerator < 0)
          numerator = -1;
      } else if(denominator > 0)
        denominator = 1;
      else if(denominator < 0)
        denominator = -1;
      return new Rational(numerator, denominator);
    },
    rec: function rec(){
      return new Rational(this.denominator, this.numerator);
    },
    gt: function gt(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 || r.denominator === 0 ?
             this.valueOf() > r.valueOf() :
             r.denominator > 0 === this.denominator > 0 ? 
             this.numerator * r.denominator > r.numerator * this.denominator :
             this.numerator * r.denominator < r.numerator * this.denominator;
    },
    ge: function ge(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 || r.denominator === 0 ?
             this.valueOf() >= r.valueOf() :
             r.denominator > 0 === this.denominator > 0 ? 
             this.numerator * r.denominator >= r.numerator * this.denominator :
             this.numerator * r.denominator <= r.numerator * this.denominator;
    },
    lt: function lt(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 || r.denominator === 0 ?
             this.valueOf() < r.valueOf() :
             r.denominator > 0 === this.denominator > 0 ? 
             this.numerator * r.denominator < r.numerator * this.denominator :
             this.numerator * r.denominator > r.numerator * this.denominator;
    },
    le: function le(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 || r.denominator === 0 ?
             this.valueOf() <= r.valueOf() :
             r.denominator > 0 === this.denominator > 0 ? 
             this.numerator * r.denominator <= r.numerator * this.denominator :
             this.numerator * r.denominator >= r.numerator * this.denominator;
    },
    eq: function eq(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 || r.denominator === 0 ?
             this.valueOf() === r.valueOf() :
             this.numerator * r.denominator === r.numerator * this.denominator;
    },
    ne: function ne(r){
      if(Rational._checkMulOverflow(this.numerator, r.denominator) ||
         Rational._checkMulOverflow(r.numerator, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 || r.denominator === 0 ?
             this.valueOf() !== r.valueOf() :
             this.numerator * r.denominator !== r.numerator * this.denominator;
    },
    gtInt: function gtInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 ?
             this.valueOf() > n :
             this.denominator > 0 ? 
             this.numerator > n * this.denominator :
             this.numerator < n * this.denominator;
    },
    geInt: function geInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 ?
             this.valueOf() >= n :
             this.denominator > 0 ? 
             this.numerator >= n * this.denominator :
             this.numerator <= n * this.denominator;
    },
    ltInt: function ltInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 ?
             this.valueOf() < n :
             this.denominator > 0 ? 
             this.numerator < n * this.denominator :
             this.numerator > n * this.denominator;
    },
    leInt: function leInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 ?
             this.valueOf() <= n :
             this.denominator > 0 ? 
             this.numerator <= n * this.denominator :
             this.numerator >= n * this.denominator;
    },
    eqInt: function eqInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 ?
             this.valueOf() === n :
             this.numerator === n * this.denominator;
    },
    neInt: function neInt(n){
      if(Rational._checkMulOverflow(n, this.denominator))
         throw new OverflowError("overflow!");
      return this.denominator === 0 ?
             this.valueOf() !== n :
             this.numerator !== n * this.denominator;
    },
    toString: function toString(){
      return this.numerator + ' / ' + this.denominator;
    },
    valueOf: function valueOf(){
      return this.numerator / this.denominator;
    }
  };

  Rational.max = function max(){
    return Array.prototype.slice.call(arguments,0,arguments.length).sort(function(a,b){
      if(Rational._checkMulOverflow(b.numerator, a.denominator) ||
         Rational._checkMulOverflow(a.numerator, b.denominator) ||
         Rational._checkSubOverflow(b.numerator * a.denominator, a.numerator * b.denominator))
         throw new OverflowError("overflow!");
      return b.numerator * a.denominator - a.numerator * b.denominator;
    })[0];
  };

  Rational.min = function min(){
    return Array.prototype.slice.call(arguments,0,arguments.length).sort(function(a,b){
      if(Rational._checkMulOverflow(a.numerator, b.denominator) ||
         Rational._checkMulOverflow(b.numerator, a.denominator) ||
         Rational._checkSubOverflow(a.numerator * b.denominator, b.numerator * a.denominator))
         throw new OverflowError("overflow!");
      return a.numerator * b.denominator - b.numerator * a.denominator;
    })[0];
  };

  Rational.floorInt = function floorInt(r){
    return r.numerator === 0 || r.denominator === 0 ? r.valueOf() :
           (r.numerator - r.numerator % r.denominator) / r.denominator -
           (r.numerator / 0 === -r.denominator / 0 && r.numerator % r.denominator ? 1 : 0);
  };

  Rational.roundInt = function roundInt(r){
    return r.numerator === 0 || r.denominator === 0 ? r.valueOf() :
           (r.numerator - r.numerator % r.denominator) / r.denominator -
           (r.numerator / 0 === -r.denominator / 0 && r.numerator % r.denominator ? 1 : 0) +
    // avoid overflow , not use a * 2 - b, use a - b + a
           ((r.denominator < 0 ? -r.numerator : r.numerator) % r.denominator - (r.numerator < 0 ? -r.denominator : r.denominator) + (r.denominator < 0 ? -r.numerator : r.numerator) % r.denominator >= 0);
  };

  Rational._checkAddOverflow = function _checkAddOverflow(r0,r1){
    if(r0 > 0 && r1 > 0)
      return 9007199254740992 - r0 < r1;
    else if(r0 < 0 && r1 < 0)
      return -9007199254740992 + r0 > r1;
    return false;
  };

  Rational._checkSubOverflow = function _checkSubOverflow(r0,r1){
    return Rational._checkAddOverflow(r0, -r1);
  };

  Rational._checkMulOverflow = function _checkMulOverflow(r0,r1){
    if(r0 === 0 || r1 === 0)
      return false;
    if(r0 < 0)
      r0 = -r0;
    if(r1 < 0)
      r1 = -r1;
    var q = (9007199254740992 - 9007199254740992 % r0) / r0;
    if(q < r1 || (q === r1 && 9007199254740992 % r0 !== 0))
      return true;
    else
      return false;
  };

  function checkStringOverflow(){
    for(var i = 0; i < arguments.length; ++i){
      var s = arguments[i];
      if(s.length > "9007199254740992".length || s.length === "9007199254740992".length && s > "9007199254740992")
        return true;
    }
    return false;
  }

  function RGBtoArcHue(red,green,blue){
    if(typeof red==="number")
      red = new Rational(red,1);
    if(typeof green==="number")
      green = new Rational(green,1);
    if(typeof blue==="number")
      blue = new Rational(blue,1);
    return (Math.atan2(green.sub(blue).divInt(2).valueOf()*Math.sqrt(3),red.sub(green.divInt(2)).sub(blue.divInt(2)).valueOf()) * 180 / Math.PI + 360) % 360;
  }

  function RGBtoHSY(r,g,b,red,green,blue,isCylinder){
    if(typeof r==="number")
      r = new Rational(r,1);
    if(typeof g==="number")
      g = new Rational(g,1);
    if(typeof b==="number")
      b = new Rational(b,1);
    if(typeof red==="number")
      red = new Rational(red,1);
    if(typeof green==="number")
      green = new Rational(green,1);
    if(typeof blue==="number")
      blue = new Rational(blue,1);
    var max = Rational.max(red,green,blue);
    var min = Rational.min(red,green,blue);
    var C = max.sub(min);
    var H;
    if(C.eqInt(0))
      H = new Rational(0,1);
    else if(max.eq(red))
      H = green.sub(blue).mulInt(60).div(C).addInt(360).modInt(360);
    else if(max.eq(green))
      H = blue.sub(red).div(C).addInt(2).mulInt(60);
    else if(max.eq(blue))
      H = red.sub(green).div(C).addInt(4).mulInt(60);
    var luma = r.mul(red).add(g.mul(green)).add(b.mul(blue)).divInt(255);
    var S;
    if(isCylinder){
      var lumaWhenMaxC;
      switch(Math.floor(H.divInt(60).valueOf())){
        case 0:
          lumaWhenMaxC = r.add(g.mul(H.modInt(60).divInt(60)));
          break;
        case 1:
          lumaWhenMaxC = g.add(r.mul(new Rational(1,1).sub(H.modInt(60).divInt(60))));
          break;
        case 2:
          lumaWhenMaxC = g.add(b.mul(H.modInt(60).divInt(60)));
          break;
        case 3:
          lumaWhenMaxC = b.add(g.mul(new Rational(1,1).sub(H.modInt(60).divInt(60))));
          break;
        case 4:
          lumaWhenMaxC = b.add(r.mul(H.modInt(60).divInt(60)));
          break;
        case 5:
          lumaWhenMaxC = r.add(b.mul(new Rational(1,1).sub(H.modInt(60).divInt(60))));
          break;
      }
      if(luma.le(lumaWhenMaxC))
        S = new Rational(1,1).sub(min.divInt(255).div(luma));
      else
        S = max.divInt(255).sub(luma).div(new Rational(1,1).sub(luma));
    } else
      S = new Rational(1,1).sub(min.divInt(255).div(luma));
    if(isNaN(H.valueOf()))
      H = new Rational(0,1);
    if(isNaN(S.valueOf()))
      S = new Rational(0,1);
    return [H,S,luma];
  }

  function OverflowError(message){
    this.message = message;
  }
  OverflowError.prototype = new Error('prototype');

  function InvalidRangeError(message){
    this.message = message;
  }
  InvalidRangeError.prototype = new Error('prototype');

  function InvalidFormatError(message){
    this.message = message;
  }
  InvalidFormatError.prototype = new Error('prototype');

  function HSYtoRGB(r,g,b,H,S,luma,isCylinder){
    if(typeof r==="number")
      r = new Rational(r,1);
    if(typeof g==="number")
      g = new Rational(g,1);
    if(typeof b==="number")
      b = new Rational(b,1);
    if(typeof H==="number")
      H = new Rational(H,1);
    if(typeof S==="number")
      S = new Rational(S,1);
    if(typeof luma==="number")
      luma = new Rational(luma,1);
    var lumaWhenMaxC;
    H = H.modInt(360).addInt(360).modInt(360);
    switch(Math.floor(H.divInt(60).valueOf())){
      case 0:
      case 6:
        lumaWhenMaxC = r.add(g.mul(H.modInt(60).divInt(60)));
        break;
      case 1:
        lumaWhenMaxC = g.add(r.mul(new Rational(1,1).sub(H.modInt(60).divInt(60))));
        break;
      case 2:
        lumaWhenMaxC = g.add(b.mul(H.modInt(60).divInt(60)));
        break;
      case 3:
        lumaWhenMaxC = b.add(g.mul(new Rational(1,1).sub(H.modInt(60).divInt(60))));
        break;
      case 4:
        lumaWhenMaxC = b.add(r.mul(H.modInt(60).divInt(60)));
        break;
      case 5:
        lumaWhenMaxC = r.add(b.mul(new Rational(1,1).sub(H.modInt(60).divInt(60))));
        break;
    }
    if(!isCylinder && S.gt(lumaWhenMaxC.mul(new Rational(1,1).sub(luma).div(new Rational(1,1).sub(lumaWhenMaxC).mul(luma)))))
      throw new InvalidRangeError('invalid range!');
    var min,max,C;
    if(!isCylinder || luma.le(lumaWhenMaxC)){
//      var min = (255 - S) * I / 255;
      min = new Rational(1,1).sub(S).mul(luma).mulInt(255);
//      var C = S * I / 255 / lumaWhenMaxC;
      C = S.mul(luma).mulInt(255).div(lumaWhenMaxC);
//      var max = C + min;
      max = C.add(min);
    } else {
//      var max = S * (255 - I) / 255 + I;
      max = S.mul(new Rational(1,1).sub(luma)).add(luma).mulInt(255);
//      var C = S * (255 - I) / 255 / (1 - lumaWhenMaxC);
      C = S.mul(new Rational(1,1).sub(luma)).mulInt(255).div(new Rational(1,1).sub(lumaWhenMaxC));
//      var min = max - C;
      min = max.sub(C);
    }
    var red, green, blue;
    switch(Math.floor(H.divInt(60).valueOf())){
      case 0:
        red = max;
        blue = min;
//        green = (I - r * red - b * blue) / g;
        green = luma.mulInt(255).sub(r.mul(red)).sub(b.mul(blue)).div(g);
        break;
      case 1:
        green = max;
        blue = min;
//        red = (I - g * green - b * blue) / r;
        red = luma.mulInt(255).sub(g.mul(green)).sub(b.mul(blue)).div(r);
        break;
      case 2:
        green = max;
        red = min;
//        blue = (I - r * red - g * green) / b;
        blue = luma.mulInt(255).sub(r.mul(red)).sub(g.mul(green)).div(b);
        break;
      case 3:
        blue = max;
        red = min;
//        green = (I - r * red - b * blue) / g;
        green = luma.mulInt(255).sub(r.mul(red)).sub(b.mul(blue)).div(g);
        break;
      case 4:
        blue = max;
        green = min;
//        red = (I - g * green - b * blue) / r;
        red = luma.mulInt(255).sub(g.mul(green)).sub(b.mul(blue)).div(r);
        break;
      case 5:
        red = max;
        green = min;
//        blue = (I - r * red - g * green) / b;
        blue = luma.mulInt(255).sub(r.mul(red)).sub(g.mul(green)).div(b);
        break;
    }
    return [red,green,blue];
  }

  function RGBtoHSV(red,green,blue){
    if(typeof red==="number")
      red = new Rational(red,1);
    if(typeof green==="number")
      green = new Rational(green,1);
    if(typeof blue==="number")
      blue = new Rational(blue,1);
    var max = Rational.max(red,green,blue);
    var min = Rational.min(red,green,blue);
    var C = max.sub(min);
    var H;
    if(C.eqInt(0))
      H = new Rational(0,1);
    else if(max.eq(red))
      H = green.sub(blue).mulInt(60).div(C).addInt(360).modInt(360);
    else if(max.eq(green))
      H = blue.sub(red).div(C).addInt(2).mulInt(60);
    else if(max.eq(blue))
      H = red.sub(green).div(C).addInt(4).mulInt(60);
    var V = max.divInt(255);
    var S = C.div(max);
    return [H,S,V];
  }

  function HSVtoRGB(H,S,V){
    if(typeof H==="number")
      H = new Rational(H,1);
    if(typeof S==="number")
      S = new Rational(S,1);
    if(typeof V==="number")
      V = new Rational(V,1);
    var C = S.mul(V).mulInt(255);
    var max = V.mulInt(255);
    var min = max.sub(C);
    var red, green, blue;
    H = H.modInt(360).addInt(360).modInt(360);
    if(H.geInt(300) || H.lt(60)){
      red = max;
      blue = min;
      var temp = H.divInt(60);
      if(H.geInt(300))
        temp = temp.sub(6);
      green = temp.mul(C).add(blue);
    } else if(H.geInt(60) && H.ltInt(180)){
      green = max;
      red = min;
      blue = H.divInt(60).subInt(2).mul(C).add(red);
    } else if(H.geInt(180) && H.lt(300)){
      blue = max;
      green = min;
      red = H.divInt(60).subInt(4).mul(C).add(green);
    }
    return [red,green,blue];
  }

  function RGBtoHSL(red,green,blue){
    if(typeof red==="number")
      red = new Rational(red,1);
    if(typeof green==="number")
      green = new Rational(green,1);
    if(typeof blue==="number")
      blue = new Rational(blue,1);
    var max = Rational.max(red,green,blue);
    var min = Rational.min(red,green,blue);
    var C = max.sub(min);
    var H;
    if(C.eqInt(0))
      H = new Rational(0,1);
    else if(max.eq(red))
      H = green.sub(blue).mulInt(60).div(C).addInt(360).modInt(360);
    else if(max.eq(green))
      H = blue.sub(red).div(C).addInt(2).mulInt(60);
    else if(max.eq(blue))
      H = red.sub(green).div(C).addInt(4).mulInt(60);
    var L = max.add(min).divInt(2).divInt(255);
    var S;
    if(L.le(new Rational(1,2)))
      S = C.divInt(255).div(L).divInt(2);
    else
      S = C.divInt(255).div(new Rational(1,1).sub(L)).divInt(2);
    return [H,S,L];
  }

  function HSLtoRGB(H,S,L){
    if(typeof H==="number")
      H = new Rational(H,1);
    if(typeof S==="number")
      S = new Rational(S,1);
    if(typeof L==="number")
      L = new Rational(L,1);
    var C;
    if(L.le(new Rational(1,2)))
      C = S.mulInt(2).mul(L).mulInt(255);
    else
      C = S.mulInt(2).mul(new Rational(1,1).sub(L)).mulInt(255);
    var max = C.divInt(2).add(L.mulInt(255));
    var min = max.sub(C);
    var red, green, blue;
    H = H.modInt(360).addInt(360).modInt(360);
    if(H.geInt(300) || H.lt(60)){
      red = max;
      blue = min;
      var temp = H.divInt(60);
      if(H.geInt(300))
        temp = temp.sub(6);
      green = temp.mul(C).add(blue);
    } else if(H.geInt(60) && H.ltInt(180)){
      green = max;
      red = min;
      blue = H.divInt(60).subInt(2).mul(C).add(red);
    } else if(H.geInt(180) && H.lt(300)){
      blue = max;
      green = min;
      red = H.divInt(60).subInt(4).mul(C).add(green);
    }
    return [red,green,blue];
  }

  var image = new Image();
  image.onload = function(){
    var width = image.width;
    var height = image.height;
    $('#canvas')[0].setAttribute('width',width);
    $('#canvas')[0].setAttribute('height',height);
    var ctx = $('#canvas')[0].getContext('2d');
    ctx.drawImage(image,0,0);
    var arr = ctx.getImageData(0,0,width,height);
    var r = new Rational(21,100);
    var g = new Rational(72,100);
    var b = new Rational(7,100);
    for(var i = 0; i < arr.data.length; i += 4){
      var HSY = RGBtoHSY(r,g,b,arr.data[i],arr.data[i+1],arr.data[i+2],true);
      var RGB = HSYtoRGB(r,g,b,HSY[0].addInt(120).modInt(360),HSY[1],HSY[2],true);
      arr.data[i] = Rational.roundInt(RGB[0]);
      arr.data[i+1] = Rational.roundInt(RGB[1]);
      arr.data[i+2] = Rational.roundInt(RGB[2]);
    }
    ctx.putImageData(arr,0,0);
    var image2 = new Image();
    image2.src = $('#canvas')[0].toDataURL();
    $('body').append($(image2));
  }
  image.src = "commentbug.gif";
});


</script>
</body>
</html>